/**
 * 两个数组，三种操作：
 * 1 s e x: 数组A[s...e]增加x
 * 2 s e x: 数组B[s...e]增加x
 * 3 s e: 查询区间[s, e]的SIGMA{AiBi}
 * 显然是线段树，令data是<A和，B和，积和>，对应相加即可
 * 令lazy是{da, db}，对于延迟操作：
 * 三项数据分别根据两个延迟标记更新接口，延迟标记本身累加即可
 */
#include <bits/stdc++.h>
using namespace std;

#include <bits/extc++.h>
using namespace __gnu_pbds;

using llt = long long;
llt const MOD = 998244353LL;

int N, Q;
vector<llt>  A, B;

llt qpow(llt a, llt n){
    a %= MOD;
    llt ret = 1;
    while(n){
        if(n & 1) ret = ret * a % MOD;
        a = a * a % MOD;
        n >>= 1;
    }
    return ret;
}

inline llt inv(llt x){return qpow(x, MOD - 2);}


struct SegTree{ // 线段树带延迟

using llt = long long;

int N;

using value_type = array<llt, 3>; // A和/B和/积和
vector<value_type> data; // 线段树

using lazy_type = pair<llt, llt>;
vector<lazy_type> lazy; // 延迟标记

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    // assert(0);
    return {(ls[0] + rs[0]) % MOD, (ls[1] + rs[1]) % MOD, (ls[2] + rs[2]) % MOD};
}

/// 从上往下计算信息，要变动
void _dn_(int t, int s, int e, const lazy_type & delta) {
    // assert(0);
    auto & d = data[t];
    auto a = d[0];
    auto b = d[1];
    auto c = d[2];
    d = {
        (a + delta.first * (e - s + 1) % MOD) % MOD, 
        (b + delta.second * (e - s + 1) % MOD) % MOD, 
        (a * delta.second % MOD + b * delta.first % MOD + delta.first * delta.second % MOD * (e - s + 1) % MOD + c) % MOD
    };
    auto & z = lazy[t];
    a = z.first, b = z.second;
    z = {(a + delta.first) % MOD, (b + delta.second) % MOD};
    return;
}

/// 初始化，清零，不用动
void init(int n) {
    data.assign((N = n) + 1 << 2, value_zero());
    lazy.assign(n + 1 << 2, lazy_zero());
}

/// 这个函数不用动
void modify(int a, int b, const lazy_type & delta){
    _modify(1, 1, N, a, b, delta);
}

/// 这个函数不用动
value_type query(int a, int b){
    return _query(1, 1, N, a, b);
}

/// 这个函数不用动
void build() {
    _build(1, 1, N);
}

/// 几乎不用动
value_type _query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    value_type ans = value_zero(); // 如果求最值，这里不能用zero
    if(a <= mid) ans = _up_(ans, _query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, _query(rson(t), mid + 1, e, a, b));
    return ans;
}

/// 几乎不用动
void _modify(int t, int s, int e, int a, int b, const lazy_type & delta) {
    if(a <= s and e <= b) {
        _dn_(t, s, e, delta);
        return;
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    if(a <= mid) _modify(lson(t), s, mid, a, b, delta);
    if(mid < b) _modify(rson(t), mid + 1, e, a, b, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 这个函数几乎不用动
void _pushDown(int t, int s, int e) {
    if(lazy_zero() == lazy[t]) return;
    auto & lz = lazy[t];
    auto ls = lson(t), rs = rson(t);
    int mid = (s + e) >> 1;

    _dn_(ls, s, mid, lz);
    _dn_(rs, mid + 1, e, lz);

    lz = lazy_zero();
}


/// 几乎不用动
void _build(int t, int s, int e) {
    if(s == e) {
        // int x; cin >> x;
        data[t] = {A[s] % MOD, B[s] % MOD, A[s] * B[s] % MOD}; // 注意提供value_type的初始化
        return; 
    }
    int mid = (s + e) >> 1;
    _build(lson(t), s, mid);
    _build(rson(t), mid + 1, e);
    _pushUp(t);
}

/// 辅助函数，视延迟的类型而变动
static const lazy_type & lazy_zero() {
    static const lazy_type LAZY0 = {0LL, 0LL};
    return LAZY0; 
}

/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0 = {0LL, 0LL, 0LL};
    return VALUE0;
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}


}St;



void work(){
    cin >> N >> Q;
    A.assign(N + 1, {});
    B.assign(N + 1, {});
    for(int i=1;i<=N;++i) cin >> A[i];
    for(int i=1;i<=N;++i) cin >> B[i];
    St.init(N);
    St.build();

    for(int cmd,q=1;q<=Q;++q){
        cin >> cmd;
        if(1 == cmd){
            int s, e; llt x; cin >> s >> e >> x;
            St.modify(s, e, {x, 0LL});
        }else if(2 == cmd){
            int s, e; llt x;  cin >> s >> e >> x;
            St.modify(s, e, {0LL, x});
        }else{
            int s, e; cin >> s >> e;
            auto ans = St.query(s, e)[2] % MOD;
            if(ans < 0) ans += MOD;
            cout << ans << "\n";
        }
    }
    return;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int nofkase = 1;
    // cin >> nofkase; 
    while(nofkase--) work();
    return 0;
}